﻿using Microsoft.Extensions.Logging;
using SkiaSharp;

namespace Devonline.AspNetCore;

/// <summary>
/// 图片水印服务
/// </summary>
/// <param name="logger"></param>
/// <param name="httpSetting"></param>
public class ImageWatermarkService(ILogger<ImageWatermarkService> logger, HttpSetting httpSetting) : BaseImageService(logger, httpSetting), IImageWatermarkService
{
    private readonly ImageWatermarkOptions _watermarkOptions = httpSetting.Watermark!;

    /// <summary>
    /// 给图片添加水印
    /// PS: 应该先缩放在加水印, 这样水印就可以在缩放后的图片上添加; 如果先加水印在缩放, 就只能先复制原图的副本, 因为不能破坏原图本身
    /// 因此添加水印的原图此时已经是缩放后的图片了, 因此添加水印的功能, 不在判断结果图是否存在, 默认已经存在
    /// </summary>
    /// <param name="fileName">图片对象</param>
    /// <returns></returns>
    public string AddWatermark(string fileName) => AddWatermark(fileName, _watermarkOptions.Watermark, _watermarkOptions.FontFamily, _watermarkOptions.FontSize, _watermarkOptions.Color, _watermarkOptions.SaveAs);

    /// <summary>
    /// 给图片添加水印
    /// 此方法无是否另存为选项, 因此会将水印添加到原图
    /// </summary>
    /// <param name="fileName">图片对象</param>
    /// <param name="watermark">水印内容</param>
    /// <param name="fontFamily">字体</param>
    /// <param name="fontSize">预设字体最大值</param>
    /// <param name="color">预设文字颜色, 包括设置透明度</param>
    /// <param name="saveAs">结果是否另存为, 另存为则保存为新文件</param>
    /// <returns></returns>
    private string AddWatermark(string fileName, string watermark, string fontFamily, int fontSize, string color, bool saveAs)
    {
        if (!File.Exists(fileName))
        {
            throw new FileNotFoundException("原文件不存在!", fileName);
        }

        //加载原始图片到内存中
        using var stream = File.OpenRead(fileName);
        using var skManagedStream = new SKManagedStream(stream);
        using var original = SKBitmap.Decode(skManagedStream);
        using var canvas = new SKCanvas(original);

        // 获取字体在字体集合中的下标
        var index = SKFontManager.Default.FontFamilies.ToList().IndexOf(fontFamily);
        if (index < 0)
        {
            _logger.LogWarning($"{_userAccess}: the font family '{fontFamily}' not found, using default font.");
            index = 0; // 使用默认字体
        }

        // 创建宋体字形
        var typeface = SKFontManager.Default.GetFontStyles(index).CreateTypeface(0);

        //设置画笔和颜色
        var skColor = GetColor(color);
        var paint = new SKPaint { Color = skColor };

        //根据文字样式计算画布宽度
        var skFontSize = (original.Width > original.Height ? original.Height : original.Width) / 10;
        if (skFontSize > fontSize)
        {
            skFontSize = fontSize;
        }

        _logger.LogInformation($"{_userAccess}: adding watermark '{watermark}' to image at {fileName} with font size {skFontSize} and color {skColor}.");

        //计算并绘制水印文字位置, 绘制方式以左下角向右上角斜向上45°方向绘制
        using SKPath path = new SKPath();
        using var font = new SKFont(typeface, size: skFontSize, scaleX: 1, skewX: 0);
        var rowLength = skFontSize * 4;
        var columnLength = skFontSize * watermark.Length;
        var row = original.Width / rowLength - 1;
        var column = ((int)Math.Ceiling(original.Height / (double)columnLength)) * 2;
        for (int i = 0; i <= row; i++)
        {
            var width = 0;
            var height = rowLength * i;
            if (height <= original.Height)
            {
                for (int j = 0; j < column; j++)
                {
                    width = skFontSize + columnLength * j;
                    path.Reset();
                    path.MoveTo(width, height);
                    path.LineTo(width + columnLength, height - rowLength);
                    canvas.DrawTextOnPath(watermark, path, 0, 0, SKTextAlign.Left, font, paint);
                }
            }
        }

        path.Close();
        canvas.Flush();
        stream.Close();

        var newFileName = saveAs ? GetNewFileName(fileName, "watermark") : fileName;
        _logger.LogInformation($"{_userAccess}: saving watermarked image to {newFileName}");
        return Save(original, newFileName);
    }
    /// <summary>
    /// 从字符串颜色值构造颜色
    /// </summary>
    /// <param name="color"></param>
    /// <returns></returns>
    private static SKColor GetColor(string color)
    {
        SKColor skColor = SKColors.Silver;
        if (color.StartsWith(CHAR_SHARP))
        {
            skColor = SKColor.Parse(color);
        }
        else if (color.StartsWith(DEFAULT_HEX_PREFIX))
        {
            skColor = new SKColor(Convert.ToUInt32(color, UNIT_SIXTEEN));
        }
        else
        {
            skColor = typeof(SKColors).GetStaticMemberValue<SKColor>(color);
        }

        return skColor;
    }
}